package top.ochiamalu.aioj.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.ochiamalu.aioj.common.ErrorCode;
import top.ochiamalu.aioj.constant.ConfigConstants;
import top.ochiamalu.aioj.constant.JudgeResultConstants;
import top.ochiamalu.aioj.converter.SetConverters;
import top.ochiamalu.aioj.domain.dto.set.AddSetDTO;
import top.ochiamalu.aioj.domain.dto.set.PageSetDTO;
import top.ochiamalu.aioj.domain.dto.set.SetDTO;
import top.ochiamalu.aioj.domain.dto.set.SetGroupDTO;
import top.ochiamalu.aioj.domain.dto.set.UpdateSetDTO;
import top.ochiamalu.aioj.domain.entity.Answer;
import top.ochiamalu.aioj.domain.entity.Config;
import top.ochiamalu.aioj.domain.entity.Question;
import top.ochiamalu.aioj.domain.entity.Set;
import top.ochiamalu.aioj.domain.entity.SetGroup;
import top.ochiamalu.aioj.domain.vo.set.SetDetailVO;
import top.ochiamalu.aioj.domain.vo.set.SetGroupQuestion;
import top.ochiamalu.aioj.domain.vo.set.SetGroupVO;
import top.ochiamalu.aioj.domain.vo.set.SetOptionVO;
import top.ochiamalu.aioj.domain.vo.set.SetVO;
import top.ochiamalu.aioj.exception.BusinessException;
import top.ochiamalu.aioj.mapper.SetMapper;
import top.ochiamalu.aioj.service.AnswerService;
import top.ochiamalu.aioj.service.ConfigService;
import top.ochiamalu.aioj.service.QuestionService;
import top.ochiamalu.aioj.service.SetGroupService;
import top.ochiamalu.aioj.service.SetService;

import javax.annotation.Resource;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 题集服务impl
 *
 * @author ochiamalu
 * @date 2025/01/28
 */
@Service
public class SetServiceImpl extends ServiceImpl<SetMapper, Set>
        implements SetService {

    private static final String DEFAULT_COVER = "https://assets.leetcode.cn/aliyun-lc-upload/study_plan_v2/dynamic-programming/cover";

    private static final Type STRING_LIST_TYPE = new TypeToken<List<String>>() {
    }.getType();

    private static final Type LONG_LIST_TYPE = new TypeToken<List<Long>>() {
    }.getType();

    @Resource
    private SetGroupService setGroupService;

    @Resource
    private QuestionService questionService;

    @Resource
    private AnswerService answerService;

    @Resource
    private ConfigService configService;

    private static final Gson GSON = new Gson();

    /**
     * 添加题集
     *
     * @param addSetDTO 添加题集到
     * @return {@link Long }
     */
    @Override
    @Transactional
    public Long addSet(AddSetDTO addSetDTO) {
        Set set = convertToSet(addSetDTO);
        List<Long> setGroupIds = processGroups(addSetDTO.getSetGroupList());
        set.setGroupId(GSON.toJson(setGroupIds));
        return this.save(set) ? set.getId() : null;
    }

    /**
     * 获取主页题集
     *
     * @return {@link List }<{@link SetVO }>
     */
    @Override
    public List<SetVO> listHome() {
        List<Config> configList = configService.lambdaQuery().eq(Config::getType, ConfigConstants.STUDY_PLAN_TYPE).list();
        if (configList == null || configList.isEmpty()) {
            return Collections.emptyList();
        }
        List<Set> setList = new ArrayList<>();
        configList.forEach(config -> {
            Set set = this.getById(config.getValue());
            if (set != null) {
                setList.add(set);
            }
        });
        return SetConverters.INSTANCE
                .convertEntity2VO(setList);
    }

    /**
     * 获取推荐题集
     *
     * @return {@link List }<{@link SetOptionVO }>
     */
    @Override
    public List<SetOptionVO> listRecommend() {
        List<Config> configList = configService.lambdaQuery().eq(Config::getType, ConfigConstants.TOOL_BAR_SET_TYPE).list();
        if (configList == null || configList.isEmpty()) {
            return Collections.emptyList();
        }
        List<Set> setList = new ArrayList<>();
        configList.forEach((config -> {
            Set set = this.getById(config.getValue());
            if (set != null) {
                setList.add(set);
            }
        }));
        return setList.stream().map((item) -> {
            SetOptionVO setOptionVO = new SetOptionVO();
            setOptionVO.setLabel(item.getTitle());
            setOptionVO.setValue(item.getId());
            return setOptionVO;
        }).collect(Collectors.toList());
    }

    /**
     * 得到题集细节
     *
     * @param id 身份证件
     * @return {@link SetDetailVO }
     */
    @Override
    public SetDetailVO getSetDetail(Long id) {
        Set set = this.getById(id);
        if (set == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "题集不存在");
        }

        SetDetailVO setDetailVO = buildBaseSetDetail(set);
        List<Long> groupIds = GSON.fromJson(set.getGroupId(), LONG_LIST_TYPE);
        if (groupIds == null || groupIds.isEmpty()) {
            return setDetailVO;
        }

        List<SetGroupVO> setGroupVOList = buildSetGroupVOList(groupIds, id);
        setDetailVO.setGroupList(setGroupVOList);
        return setDetailVO;
    }

    @Override
    public boolean updateById(UpdateSetDTO updateSetDTO) {
        Set set = convertToSet(updateSetDTO);

        String groupIds = set.getGroupId();
        if (StringUtils.isNotBlank(groupIds)) {
            List<Long> oldGroupIds = GSON.fromJson(groupIds, LONG_LIST_TYPE);
            setGroupService.removeBatchByIds(oldGroupIds);
        }

        List<Long> setGroupIds = processGroups(updateSetDTO.getSetGroupList());
        set.setGroupId(GSON.toJson(setGroupIds));

        return this.updateById(set);
    }

    @Override
    public Page<SetVO> pageSet(PageSetDTO pageSetDTO) {
        LambdaQueryWrapper<Set> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(pageSetDTO.getTagList() != null, Set::getTags, pageSetDTO.getTagList());

        Page<Set> setPage = this.page(new Page<>(pageSetDTO.getCurrentPage(), pageSetDTO.getPageSize()), wrapper);
        Page<SetVO> setVOPage = new Page<>();
        BeanUtils.copyProperties(setPage, setVOPage, "records");
        List<SetVO> setVOList = SetConverters.INSTANCE
                .convertEntity2VO(setPage.getRecords());
        setVOPage.setRecords(setVOList);
        return setVOPage;
    }

    @Override
    public boolean deleteSet(Long id) {
        Set set = this.getById(id);
        if (set == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "题集不存在");
        }
        boolean removed = this.removeById(id);
        if (!removed) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除失败");
        }
        String groupId = set.getGroupId();
        List<Long> groupIdList = GSON.fromJson(groupId, LONG_LIST_TYPE);
        if (groupIdList == null || groupIdList.isEmpty()) {
            return true;
        }
        return setGroupService.removeBatchByIds(groupIdList);
    }

    private SetDetailVO buildBaseSetDetail(Set set) {
        SetDetailVO setDetailVO = new SetDetailVO();
        setDetailVO.setId(set.getId());
        setDetailVO.setTitle(set.getTitle());
        setDetailVO.setCover(set.getCover());
        setDetailVO.setTarget(set.getTarget());
        setDetailVO.setDescription(set.getDescription());
        setDetailVO.setTagList(GSON.fromJson(set.getTags(), STRING_LIST_TYPE));
        return setDetailVO;
    }

    private List<SetGroupVO> buildSetGroupVOList(List<Long> groupIds, Long setId) {
        return groupIds.stream()
                .map(groupId -> {
                    SetGroup setGroup = setGroupService.getById(groupId);
                    SetGroupVO setGroupVO = new SetGroupVO();
                    setGroupVO.setTitle(setGroup.getTitle());

                    List<Long> questionIds = GSON.fromJson(setGroup.getQuestionId(), LONG_LIST_TYPE);
                    List<SetGroupQuestion> questions = buildSetGroupQuestions(questionIds, setId);
                    setGroupVO.setQuestionList(questions);
                    return setGroupVO;
                })
                .collect(Collectors.toList());
    }

    private List<SetGroupQuestion> buildSetGroupQuestions(List<Long> questionIds, Long setId) {
        return questionIds.stream()
                .map(questionId -> {
                    Question question = questionService.getById(questionId);
                    SetGroupQuestion groupQuestion = new SetGroupQuestion();
                    groupQuestion.setId(questionId);
                    groupQuestion.setTitle(question.getTitle());
                    groupQuestion.setDifficulty(question.getDifficulty());
                    groupQuestion.setTagList(buildQuestionTags(question));
                    groupQuestion.setStatus(determineQuestionStatus(questionId, setId));
                    return groupQuestion;
                })
                .collect(Collectors.toList());
    }

    private List<String> buildQuestionTags(Question question) {
        List<String> tags = new ArrayList<>();
        tags.add("#" + question.getId());
        if (StringUtils.isNotBlank(question.getTags())) {
            tags.addAll(GSON.fromJson(question.getTags(), STRING_LIST_TYPE));
        }
        return tags;
    }

    private int determineQuestionStatus(Long questionId, Long setId) {
        Long answerCount = answerService.lambdaQuery()
                .eq(Answer::getQuestionId, questionId)
                .eq(Answer::getSetId, setId)
                .count();

        if (answerCount == 0) {
            return 0;
        }

        List<Answer> answers = answerService.lambdaQuery()
                .eq(Answer::getQuestionId, questionId)
                .eq(Answer::getSetId, setId)
                .orderBy(true, false, Answer::getCreateTime)
                .list();

        return answers.get(0).getStatus().equals(JudgeResultConstants.ACCEPTED) ? 2 : 1;
    }

    private Set convertToSet(SetDTO setDTO) {
        Set set = new Set();
        set.setTitle(setDTO.getTitle());
        set.setCover(StringUtils.isNotBlank(setDTO.getCover()) ? setDTO.getCover() : DEFAULT_COVER);
        set.setTarget(setDTO.getTarget());
        set.setDescription(setDTO.getDescription());

        if (setDTO.getTagList() != null) {
            set.setTags(GSON.toJson(setDTO.getTagList()));
        }

        if (setDTO instanceof UpdateSetDTO) {
            set.setId(((UpdateSetDTO) setDTO).getId());
        }

        return set;
    }

    private List<Long> processGroups(List<SetGroupDTO> groups) {
        return groups.stream().map(group -> {
            SetGroup setGroup = new SetGroup();
            setGroup.setTitle(group.getTitle());
            setGroup.setQuestionId(GSON.toJson(group.getQuestionIdList()));
            setGroupService.save(setGroup);
            return setGroup.getId();
        }).collect(Collectors.toList());
    }
}
